
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irq.h>

#include <asm/ioctl.h>

#include <asm/uaccess.h>
#include <mach/gpio.h>
#include <linux/pwm.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>

#include <linux/pmic_external.h>

#include "imxdriver.h"
#include "spi_slave.h"



#define I2C_ADPATER_NO 1





static int gpiotest_major=0;
static struct class *gpiotest_class=0;

//extern void ita_set_sens_conf(unsigned int sens_conf);
extern void __gpio_set_value(unsigned gpio, int value);
extern int __gpio_get_value(unsigned gpio);
//extern int _gpio_direction_input(IOMUX_TO_GPIO(MX35_PIN_ATA_DATA13));
extern int gpio_direction_input(unsigned gpio);
extern int gpio_direction_output(unsigned gpio, int value);
//extern int lq035_control(unsigned char reg, unsigned short value);

extern int ipu_init_sync_panel(int disp, uint32_t pixel_clk,
			    uint16_t width, uint16_t height,
			    uint32_t pixel_fmt,
			    uint16_t h_start_width, uint16_t h_sync_width,
			    uint16_t h_end_width, uint16_t v_start_width,
			    uint16_t v_sync_width, uint16_t v_end_width,
			    uint32_t v_to_h_sync, ipu_di_signal_cfg_t sig);



//extern  void mc13892_power_off(void);
//struct pwm_device *pwm_dev=0;

//extern int adxl34x_aux_read(ACCXYZ_t *paxis);

//void ipu_dump_registers(void);

struct spi_device *m_spi=0;
unsigned int m_hspi=0;
struct clk *m_spi_clk=0;



int emza_spi_write(char *msg,int length){

	int ret=0;
	if(m_hspi){
		//ret= spi_write(m_spi, (u8 *) msg, length);	
		ret = SpiSlave_Write(m_hspi,msg,length);
	//	printk(KERN_INFO "%d = %s.%d \n",ret,__func__,__LINE__);				
	}	
	return ret;
}


int emza_spi_read(char *msg,int length){

	int ret=0;
	if(m_hspi){
		//ret= spi_read(m_spi, (u8 *) msg, length);	
		ret = SpiSlave_Read(m_hspi,msg,length);
		//printk(KERN_INFO "%d = %s.%d \n",ret,__func__,__LINE__);		
	}	
	return ret;
}
unsigned int emza_spi_status(void){
	
	if(m_hspi){
		return SpiSlave_Status(m_hspi);
	}	
	return 0;
}










static int mxc_spi_probe(struct platform_device *pdev)
{
	m_spi_clk = clk_get(&pdev->dev, "cspi_clk");
	int ret=0;
//	clk_set_rate(m_spi_clk,108000000);
	clk_enable(m_spi_clk);
//	printk(KERN_INFO " %s SETCLOK(%d) \n\r",__func__,108000000);
	//ret = SpiSlave_Init(0,0x110110,32 ,&m_hspi );
	ret = SpiSlave_Init(0,0,32 ,&m_hspi );
	printk(KERN_INFO " %s : spi clk(0x%x) speed(%d)\n",__func__,m_spi_clk,clk_get_rate(m_spi_clk));
	return 0;
}
	
	
static int mxc_spi_remove(struct platform_device *pdev){
	printk(KERN_INFO " %s : spi \n",__func__);
	if(m_hspi ){
		SpiSlave_Term(m_hspi );
		m_hspi  =0;
	}	
	return 0;
}

	



static struct platform_driver mxc_spi_driver = {
	.driver = {
		   .name = "mxc_spi",
		   .owner = THIS_MODULE,
		   },
	.probe = mxc_spi_probe,
	.remove = mxc_spi_remove,
};






#if 0

typedef struct{
 int pollflag;
 int keyeventid;
 unsigned int keys_state;
 unsigned int eventtime;
}keyinfo_t;


typedef struct{
	struct work_struct task;
	int irq;
	int key_pin;
	int key_state;
	int keyin;
	int keyout;
	int keycount;
	int adapter;
	struct i2c_adapter *adap;
	keyinfo_t keyqueue[MAX_KEY_QUEUE];
}keyshandler_t;


#define EXPENDER_IRQ (2*32 + 9)
#define I2C_KEY_ADDR 0x20


static wait_queue_head_t keyshandler_queue;
static keyshandler_t *m_keyshandler=0;
static int m_waitcond=0;
static spinlock_t m_lock; 



static irqreturn_t keyshandler_interrupt(int irq, void *dev_id){
keyshandler_t *keyshandler= (keyshandler_t *)dev_id;
	  spin_lock(m_lock);
	  schedule_work(&keyshandler->task);
	  spin_unlock(m_lock);
	  return IRQ_HANDLED; 
}


int i2c_key_write(unsigned short value){
	struct i2c_msg msg={0};	
	int ret;
	char  buffer[12];
	int i=0;
	msg.addr  = I2C_KEY_ADDR;
	msg.flags = 0;
	msg.len   = 2;
	msg.buf   = (unsigned char*)&value;
	ret = i2c_transfer(m_keyshandler->adap, &msg, 1);
	return ret;
}



static void work_keyshandler(struct work_struct *work){
		m_waitcond  =1;
	
		//printk(KERN_INFO "%s.%d   \n",__func__,__LINE__);
		spin_lock(m_lock);
	
		msg.addr  = I2C_KEY_ADDR;
		msg.flags = i2cval->flags;
		msg.len   = i2cval->count;
		msg.buf = (char *)i2cval->buf;
		ret = i2c_transfer(adap, &msg, 1);
		
		//printk(KERN_INFO "%s.%d   \n",__func__,__LINE__);
  		wake_up_interruptible(&keyshandler_queue);
		spin_unlock(m_lock);
}


int Init_Keyhandler(void){
	int ret;
	if(m_keyshandler ==0){
		m_keyshandler = kzalloc(sizeof(*m_keyshandler), GFP_KERNEL);
	}	
	if(m_keyshandler==0){
		printk(KERN_INFO "Error: %s.%d alloc memory fail \n",__func__,__LINE__);
		goto exiterror;
	}	
	spin_lock_init(&m_lock);
	m_keyshandler->irq = (128 + EXPENDER_IRQ);
	set_irq_type(m_keyshandler->irq, IRQF_TRIGGER_FALLING );
		/* register interrupt handle */
	m_keyshandler->adapter = I2C_ADPATER_NO;
	m_keyshandler->adap = i2c_get_adapter(i2cval->adapter);

		
	ret = request_irq(m_keyshandler->irq, keyshandler_interrupt,IRQF_TRIGGER_FALLING , DEVICE_NAME, m_keyshandler);
		
	
	return 0;
exiterror:
	return -1;
}	

#endif


/*********************************************************
**********************************************************/
static int driver_ioctl_switch(struct inode *inode, struct file *file, unsigned int ioctlnr, void *arg){
	int ret=0;
	//printk(KERN_INFO "%s idin(%d) Ver 2\n",__func__,ioctlnr);
	switch(ioctlnr){
	
	case DRIVER_gpio_get_value			:
	     if(arg){
			GPIOVAL_t *gpioval = (GPIOVAL_t *)arg;
			gpioval->val= __gpio_get_value(gpioval->gpio);
		}
		 break;
		
	case DRIVER_gpio_set_value			:
		 if(arg){
			GPIOVAL_t *gpioval = (GPIOVAL_t *)arg;
			__gpio_set_value(gpioval->gpio,gpioval->val);
		}
		break;
	case DRIVER_gpio_dir				:
		 if(arg){
			GPIOVAL_t *gpioval = (GPIOVAL_t  *)arg;
			switch(gpioval->dir){
			   case GPIO_DIR_INPUT 	:
			     gpio_direction_input(gpioval->gpio);
			   break;
			   case GPIO_DIR_OUTPUT :
			      gpio_direction_output(gpioval->gpio,gpioval->val);
			   break;
			}
		}
		 break;
		
	   
	case DRIVER_I2C_IO :
		if(arg)
		{	
			I2CVAL_t *i2cval = (I2CVAL_t *)arg;
			struct i2c_adapter *adap;
			struct i2c_msg msg;
			adap = i2c_get_adapter(i2cval->adapter);
			if(adap ==0){
				printk(KERN_INFO "%s 0= i2c_get_adapter\n",__func__);		
				break;
			}	
			msg.addr  = i2cval->addr;
			msg.flags = i2cval->flags;
			msg.len   = i2cval->count;
			msg.buf = (char *)i2cval->buf;
			ret = i2c_transfer(adap, &msg, 1);
			//printk(KERN_INFO "%s %d= i2c_transfer\n",__func__,ret);
			if(msg.flags & I2C_M_RD){
							
			}
			i2c_put_adapter(adap);
		}
		break;
	case DRIVER_DUMP_IPU_REGS :	
		// ipu_dump_registers();	
		 break;
	case DRIVER_WRITE_REG32 	   :
		if(arg)
		{	
		REGVAL_t *regval = (REGVAL_t *)arg;
		 volatile unsigned int  *reg = ioremap(regval->addr, SZ_4K);	
		 *reg = regval->val;
		 //printk(KERN_INFO "write: reg(%x) val(%x) \n\r",regval->addr,regval->val); 
		 iounmap(reg);
		}
		 break;	
		 
	case DRIVER_READ_REG32 	   :
		if(arg)
		{	
		REGVAL_t *regval = (REGVAL_t *)arg;
		 volatile unsigned int  *reg =  ioremap(regval->addr, SZ_4K);	
		 regval->val = *reg;
		// printk(KERN_INFO "read: reg(%x) val(%x) \n\r",regval->addr,regval->val); 
		 iounmap(reg);
		}
		 break;			 

	case DRIVER_REG_WRITE_SPI  :		
		if(arg){
			REGVAL_t *regval = (REGVAL_t *)arg;
			//ret =spi_control(m_spi_device,(unsigned char)regval->addr,(unsigned short)regval->val);
			//lq035_control((unsigned char)regval->addr,(unsigned short)regval->val);
			//printk(KERN_INFO "driver: write: reg(%x) val(%x) \n\r",regval->addr,regval->val); 
		}	
		break;	 
#ifdef SENS_CFG		
	case DRIVER_GSENS_CONF :
		if(arg){
			unsigned int *regval = arg;
			ita_set_sens_conf(*regval);
		}
		break;
#endif		
	case DRIVER_IPU_DISP :
		if(arg){
			ipudisp_t * pdisp = (ipudisp_t *)arg;
			ipu_di_signal_cfg_t sig={0};
			int statm=0;
//			ipu_init_sync_panel(pdisp->disp,pdisp->pixclock,pdisp->w,pdisp->h,pdisp->fmt,
//			pdisp->left_margin,pdisp->hsync_len,pdisp->right_margin,pdisp->upper_margin,
//			pdisp->vsync_len,pdisp->lower_margin,0,sig);
		}
		break;
#ifdef PMIC_IO		
		case DRIVER_PMIC_READ	:
			
			if(arg){	
				REGVAL_t *regval = (REGVAL_t *)arg;
				ret = pmic_read_reg(regval->addr,&regval->val,-1);
		}
		 break;			 
		case DRIVER_PMIC_WRITE	:
			if(arg){	
				REGVAL_t *regval = (REGVAL_t *)arg;
				ret = pmic_write_reg(regval->addr,regval->val,-1);			
			}
		break;
#endif	

		
		case DRIVER_SPI_CLOCK :{
			struct clk *clk=0;
			int *enable = (int*)arg;
			if(m_spi_clk ==0)
				break;
			if(*enable){
				clk_enable(m_spi_clk);
				printk(KERN_INFO "%s: clk enable (%x)\n\r",__func__,m_spi_clk); 
			}
			else{
				clk_disable(m_spi_clk);
				printk(KERN_INFO "%s: clk disable (%x) \n\r",__func__,m_spi_clk); 	
				}
			}
		break;	

		case DRIVER_SPI_WRITE :
			if(arg){	
				SPIIO_t *pspi= (SPIIO_t *)arg;
				ret = emza_spi_write(pspi->buff,pspi->length);
			}			
		break;	
		case DRIVER_SPI_READ  :	
			if(arg){	
				SPIIO_t *pspi= (SPIIO_t *)arg;
				ret = emza_spi_read(pspi->buff,pspi->length);
				pspi->length = ret;
			}			
		break;	
		case DRIVER_SPI_STATUS :{
			unsigned int *status = (unsigned int*)arg;			
			*status = emza_spi_status();
		}
		break;	
		
		case DRIVER_ACC_GXYZ :
			
			if(arg){	
				ACCXYZ_t *pxyz = (ACCXYZ_t *)arg;
				//ret = adxl34x_aux_read(pxyz);
			}
		break;				
	}	
	
	return ret;	
}



int usercopy(struct inode *inode, struct file *file,
	       unsigned int cmd, unsigned long arg,
	       int (*func)(struct inode *inode, struct file *file,
			   unsigned int cmd, void *arg))
{
	char	sbuf[128];
	void    *mbuf = NULL;
	void	*parg = NULL;
	int	err  = -EINVAL;
	


	/*  Copy arguments into temp kernel buffer  */
	switch (_IOC_DIR(cmd)) {
	case _IOC_NONE:
		parg = NULL;
		break;
	case _IOC_READ:
	case _IOC_WRITE:
	case (_IOC_WRITE | _IOC_READ):
		if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
			parg = sbuf;
		} else {
			/* too big to allocate from stack */
			mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
			if (NULL == mbuf)
				return -ENOMEM;
			parg = mbuf;
		}

		err = -EFAULT;
		if (_IOC_DIR(cmd) & _IOC_WRITE){
			//printk(KERN_INFO "%s.%d argsize(%d)\n",__func__,__LINE__,_IOC_SIZE(cmd));
			if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))){
				printk(KERN_INFO "%s.%d copy from user fail ? \n",__func__,__LINE__);
				goto out;
			}
		}
		break;
	}
	

	/* call driver */
	err = func(inode, file, cmd, parg);
	if (err == -ENOIOCTLCMD)
		err = -EINVAL;
	
	if (err < 0)
		goto out;

/*  Copy results into user buffer  */
	switch (_IOC_DIR(cmd))
	{
	case _IOC_READ:
	case (_IOC_WRITE | _IOC_READ):
		//printk(KERN_INFO "%s.%d argsize(%d)\n",__func__,__LINE__,_IOC_SIZE(cmd));
		if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))){
			err = -EFAULT;
			
		}
		break;
	}


out:
	if(mbuf)
		kfree(mbuf);
	return err;
}



static int driver_ioctl(struct inode *inode, struct file *file,
			    unsigned int cmd, unsigned long arg)
{
	
	return usercopy(inode, file, cmd, arg, driver_ioctl_switch);
}





/*!
 * This function implements the open method on a Pmic power device.
 *
 * @param        inode       pointer on the node
 * @param        file        pointer on the file
 * @return       This function returns 0.
 */
static int driver_open(struct inode *inode, struct file *file)
{
	//printk(KERN_INFO "%s : \n",__func__);
	return 0;
}

/*!
 * This function implements the release method on a Pmic power device.
 *
 * @param        inode       pointer on the node
 * @param        file        pointer on the file
 * @return       This function returns 0.
 */
static int driver_release(struct inode *inode, struct file *file)
{
	//printk(KERN_INFO "%s : \n",__func__);
	return 0;
}


static struct file_operations gpiotest_fops = {
	.owner = THIS_MODULE,
	.ioctl = driver_ioctl,
	.open = driver_open,
	.release =driver_release,
};





static int __init mydriver_init(void)
{
	int ret = 0;
	struct device *temp_class=0;
	struct clk *clk=0;

	gpiotest_major= register_chrdev(gpiotest_major, DRIVER_NAME, &gpiotest_fops);
	if (gpiotest_major < 0) {
		printk(KERN_ERR "%s: unable to get a major for \n",__func__);
		ret = -EBUSY;
		goto error;
	}

	gpiotest_class = class_create(THIS_MODULE, DRIVER_NAME);
	if (IS_ERR(gpiotest_class)) {
		ret = PTR_ERR(gpiotest_class);
		goto err_out_chrdev;
	}

	temp_class = device_create(gpiotest_class, NULL, MKDEV(gpiotest_major, 0),NULL, DRIVER_NAME);
	
	if (IS_ERR(temp_class)) {
		ret = PTR_ERR(temp_class);
		goto err_out_class;
	}

	

	//platform_driver_unregister(&mxc_spi_driver);
    platform_driver_register(&mxc_spi_driver);
	
	//emza_spi_probe(m_spi);
	
//	ret = spi_register_driver(&emza_spi_driver);
	
	printk(KERN_INFO " %s : successfully load \n",DRIVER_NAME);
	return ret;

  err_out_class:
	device_destroy(gpiotest_class, MKDEV(gpiotest_major, 0));
	class_destroy(gpiotest_class);
    err_out_chrdev:
	unregister_chrdev(gpiotest_major, DRIVER_NAME);
    error:
	return -1;
}

static void __exit mydriver_exit(void)
{


	if (gpiotest_major > 0) {
		device_destroy(gpiotest_class, MKDEV(gpiotest_major, 0));
		class_destroy(gpiotest_class);
		unregister_chrdev(gpiotest_major, DRIVER_NAME);
		gpiotest_major = 0;
		platform_driver_unregister(&mxc_spi_driver);
		
	}

	printk(KERN_INFO "%s : successfully unloaded\n",DRIVER_NAME);
}

/*
 * Module entry points
 */

subsys_initcall(mydriver_init);
module_exit(mydriver_exit);

MODULE_DESCRIPTION("gpiotest driver");
MODULE_AUTHOR("I.L Softawer, Inc.");
MODULE_LICENSE("GPL");
